package tests;

import java.io.*; 

import global.*;
import bufmgr.*;
import diskmgr.*;
import heap.*;
import iterator.*;
import index.*;

import java.util.Random;


class MYSortTest extends TestDriver 
implements GlobalConst {

	private static int   LARGE = 1000; 
	private static int   SORTPGNUM = 12; 
	private static Random rand = new Random((long)1000);


	public MYSortTest() {
		super("sorttest");
	}

	public boolean runTests ()  {

		SystemDefs sysdef = new SystemDefs( dbpath, 300, 20, "Clock" );

		// Kill anything that might be hanging around
		String newdbpath;
		String newlogpath;
		String remove_logcmd;
		String remove_dbcmd;
		String remove_cmd = "/bin/rm -rf ";

		newdbpath = dbpath;
		newlogpath = logpath;

		remove_logcmd = remove_cmd + logpath;
		remove_dbcmd = remove_cmd + dbpath;

		// Commands here is very machine dependent.  We assume
		// user are on UNIX system here
		try {
			Runtime.getRuntime().exec(remove_logcmd);
			Runtime.getRuntime().exec(remove_dbcmd);
		} 
		catch (IOException e) {
			System.err.println (""+e);
		}

		remove_logcmd = remove_cmd + newlogpath;
		remove_dbcmd = remove_cmd + newdbpath;

		//This step seems redundant for me.  But it's in the original
		//C++ code.  So I am keeping it as of now, just in case I
		//I missed something
		try {
			Runtime.getRuntime().exec(remove_logcmd);
			Runtime.getRuntime().exec(remove_dbcmd);
		} 
		catch (IOException e) {
			System.err.println (""+e);
		}

		//Run the tests. Return type different from C++
		System.out.println("Sort Test 1: Sorting on vector ascending.");
		boolean _pass = sortTest(0, 0, "testv1.in");

		System.out.println("Sort Test 2: Sorting on vector descending.");
		_pass = sortTest(1, 0, "testv2.in") && _pass;

		System.out.println("Sort Test 3: Sorting on vector ascending, returing top 10.");
		_pass = sortTest(0, 10, "testv3.in") && _pass;

		//Clean up again
		try {
			Runtime.getRuntime().exec(remove_logcmd);
			Runtime.getRuntime().exec(remove_dbcmd);
		} 
		catch (IOException e) {
			System.err.println (""+e);
		}

		System.out.println ("\n" + "...Vector Sorting tests ");
		System.out.println (_pass==OK ? "completely successfully" : "failed");
		System.out.println (".\n\n");

		return _pass;
	}

	public static AttrType[] getAttrTypeFromFldSpec(AttrType[] attrType, FldSpec[] projlist)
	{
		AttrType[] types = new AttrType[projlist.length];
		for (int i=0; i<projlist.length; i++)
		{
			types[i] = new AttrType(attrType[projlist[i].offset-1].attrType);
		}
		return types;
	}

	private boolean sortTest(int sortorder, int k, String filename)
	{
		boolean status = OK;

		AttrType[] attrType = new AttrType[3];
		attrType[0] = new AttrType(AttrType.attrInteger);
		attrType[1] = new AttrType(AttrType.attrVector100D);
		attrType[2] = new AttrType(AttrType.attrInteger);
		TupleOrder[] order = new TupleOrder[2];
		order[0] = new TupleOrder(TupleOrder.Ascending);
		order[1] = new TupleOrder(TupleOrder.Descending);

		// Create unsorted data file 
		RID             rid;
		Heapfile        f = null;
		try 
		{
			f = new Heapfile(filename);
		}
		catch (Exception e) {
			status = FAIL;
			e.printStackTrace();
		}
		// prep tuple
		Tuple t = new Tuple();

		try {
			t.setHdr((short) 3, attrType, null);
		}
		catch (Exception e) {
			System.err.println("*** error in Tuple.setHdr() ***");
			status = FAIL;
			e.printStackTrace();
		}

		int size = t.size();

		t = new Tuple(size);
		try {
			t.setHdr((short) 3, attrType, null);
		}
		catch (Exception e) {
			status = FAIL;
			e.printStackTrace();
		}

		// insert some random records
		int inum;
		Vector100Dtype vector;

		for (int i=0; i<1000; i++) 
		{
			// setting fields
			inum = rand.nextInt(1000);
			vector = getRandomVector();
			try 
			{
				t.setIntFld(1, inum);
				t.set100DVectFld(2, vector);
				t.setIntFld(3, inum+1);
			}
			catch (Exception e) {
				status = FAIL;
				e.printStackTrace();
			}

			try 
			{
				rid = f.insertRecord(t.returnTupleByteArray());
			}
			catch (Exception e) {
				status = FAIL;
				e.printStackTrace();
			}
		}

		// create an iterator by open a file scan
		FldSpec[] projlist = new FldSpec[2];
		RelSpec rel = new RelSpec(RelSpec.outer); 
		projlist[0] = new FldSpec(rel, 2);
		projlist[1] = new FldSpec(rel, 3);

		PCounter.initialize();
		
		FileScan fscan = null;

		try {
			fscan = new FileScan(filename, attrType, null, (short) 3, 2, projlist, null);
		}
		catch (Exception e) {
			status = FAIL;
			e.printStackTrace();
		}

		// we need a target vector for the sort
		Vector100Dtype target = getRandomVector();
		Vector100Dtype temp;
		double current = 0, previous = 0;
		boolean flag = true;
		int count = 0;
		t = null;

		Sort sort = null;
		try
		{
			AttrType[] newTypes = getAttrTypeFromFldSpec(attrType, projlist);
			sort = new Sort(newTypes, (short) 2, null, fscan, 1, order[sortorder], 200, 12, target, k);
		}
		catch (Exception e) 
		{
			status = FAIL;
			e.printStackTrace();
		}

		try
		{
			t = sort.get_next();
		}
		catch (Exception e) 
		{
			status = FAIL;
			e.printStackTrace();
		}

		if (t!=null)
		{
			try
			{
				temp = t.get100DVectFld(1);
				previous = Vector100Dtype.Distance(target, temp);
			}
			catch (Exception e) 
			{
				status = FAIL;
				e.printStackTrace();
			}
		}

		while (t!=null)
		{
			count++;
			try
			{
				temp = t.get100DVectFld(1);
				current = Vector100Dtype.Distance(target, temp);
				if (k > 0)
				{
					System.out.format("%d Distance: %f ", count, current);
					System.out.println(temp.toString());
				}
			}
			catch (Exception e) 
			{
				status = FAIL;
				e.printStackTrace();
			}
			if ((current < previous && sortorder == 0) || (current > previous && sortorder == 1))
			{
				System.err.format("Sorting Test: Not sorted correctly at %d. Current: %f Previous: %f\n", 
						count, current, previous);
				flag = false;
				status = FAIL;
				//break;
			}
			previous = current;
			try
			{
				t = sort.get_next();
			}
			catch (Exception e) 
			{
				status = FAIL;
				e.printStackTrace();
			}
		}

		if ( (k==0 && count<1000) || count < k)
		{
			flag = false;
			status = FAIL;
			System.err.format("Not enough records read: %d\n", count);
		}

		if (status && flag)
		{
			System.out.println("Sorting Test: Sorting of vector field OK");
		}

		// clean up
		try 
		{
			sort.close();
		}
		catch (Exception e) {
			status = FAIL;
			e.printStackTrace();
		}
		
		System.out.format("Pages read during sort: %d\n", PCounter.counter);
		return status;
	}

	private Vector100Dtype getRandomVector()
	{
		Vector100Dtype vector = new Vector100Dtype();
		short val;

		for (int i=0;i<100;i++)
		{
			val = (short)rand.nextInt(10001);
			if (rand.nextInt(100) < 50)
			{
				val = (short) -val;
			}
			vector.setIndex(i, val);
		}
		return vector;
	}
}
